生成器与迭代器我们已经学习了Python的对象,其实在Python中的所有东西都可以认为是对象,因此,我们就可以用生成器(generator)和迭代器(iterator)做到
生成器利用生成器表达式创建生成器一个最简单的创建生成器的方法与我们之前学的推导式其实差不多,与之不同的是,利用推导式时,他会将所有符合条件的列表元素全部加载到内存中,一旦数据量十分大,例如百万量级,内存就会吃不消了
我们可以利用生成器表达式来创造一个生成器,实际上就是把推导式的方括号变成圆括号,例如
12345678a = [x**2 for x in range(10) if x % 2 == 0]print(a)print(type(a))b = (x**2 for x in range if x % 2 == 0)print(b)print(type(b))print(list(b))
利用类型转换可以将生成器转换为列表进行输出,除此之外,我们还可以利用全局内置函数next(),他会从生成器的第一个元素开始,每次返回当前元素的值,并且自动指向下一个元素,直到输出最后一个元素,会抛出StopIte...
priority_queue的基本介绍这个priority_queue翻译成中文就是优先级队列,但其实我们很难去一眼看出他的意思到底是什么,他的逻辑结构实际上类似于数据结构中的堆(heap),而且是大根堆,即为堆顶为序列的最大值
堆(heap)堆实际上是一种特殊的二叉树,他最最特殊的点在于可以用数组来存储数据
普通的二叉树是不适合用数组来存储的因为可能会存在大量的空间浪费,而对于完全二叉树更适合用顺序结构存储
堆的概念与结构学术化的定义堆的概念过于难以理解,我们形象化的来理解他
一棵完全二叉树,他的任意一个节点值总是不大于或者不小于他的父节点的值
若堆顶为最大元素,则称为大根堆,若堆顶为最小元素,则称为小根堆
我们可以按照层次遍历的顺序对二叉树的所有节点进行标号,我们可以发现
$ parent = (child -1) / 2$
$child = parent*2+1$
因此我们可以把它放入数组中,例如
这里我们演示了一个小根堆的逻辑结构和存储结构
对于堆的实现来说,他有两个主要的调整算法,向上调整和向下调整算法
当构建堆时,我们采用向上调整算法,...
面向对象在C++面向过程与面向对象的章节里,我们介绍过面向过程关注的是做一件事情的需要的步骤有哪些,通过一系列函数之间的调用配合来实现解决问题,面向对象关注的是解决这一个问题参与的对象,依靠对象之间的交互来完成问题的解决
对于Python或者说很多的编程语言来说,面向对象已经是不可或缺的一部分了
对于Python来说,这个对象实际上指的是类(class),类中可以包含成员变量,成员方法,这都是属于同一个类的内容
类的定义和使用我们可以用类去封装一系列的变量和函数,基于类去构造出对象来使用
类的定义语法
1234class 类名称: 成员变量 成员方法
这里的成员方法其实就是函数,但是在类的内部,我们统称为方法
类的创建(实例化)语法
1对象名称 = 类名称()
在类定义的时候,实际上是不消耗内存空间的,因为并没有构建类的对象,他就像是一个设计图纸,并未造出实体,一旦我们创建类,或者是实例化一个类,就是产生了一个类的对象,就是消耗了内存的
例如,我们构建一个简单的学生类,定义一个方法让他做自我介绍
1234567class student: name ...
stack与queue的基本介绍在学习和实现完vector,list等容器之后,stack和queue实际上也比较容易去实现
对于这两个容器来说,他们与vector和list有着本质上的区别,因为vector和list实际上是基础的容器,分别对应着顺序表和链表这两个基础的逻辑结构,而对于stack和queue来说,他们可以理解为拥有特定性质的基础容器,可以暂且理解为相对高级的容器
这一点我们可以从他们的模板类声明中得到验证
我们可以看到,他们对模板的声明不同
前两个对应的是allocator,这其实是一种空间配置器,是负责封装堆内存管理的对象,本质上来说他是从内存池中分配内存到vector中,在使用的时候也无需配置,因为已经赋予了他缺省值
后两个是container,是容器适配器,他本质上是用另一种容器来初始化这个容器,举个例子,理论上来说,我们可以实现顺序栈和链栈,或者顺序队列和链队列,而他们正是由底层的顺序表或者链表来构成的,因此这个container则代表着实现他们底层的容器
由此我们可以看到实现stack和queue的缺省值是一个名为deque的容器,稍后我们会对...
模块与包模块的导入可以说,Python的灵魂之一就是他丰富多样的库,这些就是他人已经写好的模块化的工具,使用者只需要调用,避免造轮子的工作,例如NumPy,Matplotlib等
模块在本质上来说就是一个Python文件
在使用之前,我们需要使用导入模块的语句
1import 模块名 [as 别名] # 这里的方括号表示可选项
当你想使用这个模块中的函数时,只需要
1模块名.函数名
即可
我们为了之后对模块的各个函数调用方便,可以取一个简单的别名,例如NumPy->np,Pandas->pd等
还有一种导入的方式是
1from 模块 import *
这样是直接导入了模块中的所有函数,当然也可以将*替换成你想要的对象或者函数
这样在你使用他的时候就可以像Python自带的函数一样,不需要写模块名称了
编写自己的模块对于C和C++抑或是Java等各式各样的语言,都有一个main函数作为所有程序的入口,但是对于Python来说,他对于一个Python文件就是从头到尾依次解释执行,没有统一的入口
但是模块也是一个Python文件,我们在导入之后,并不希望导入的函数依次...
文件操作文件编码编码指的是把文字翻译成二级制的一个过程,有许多不同的对应规则,也就有许多不同的编码规范
例如,UTF-8,GBK,Big5,Unicode等
所谓乱码就是使用了错误的编码方式打开了文件的内容(大概),因此我们也可以选择编码的方式
目前UTF-8是全球通用的编码格式,一般来说文件内容都是UTF-8的
文件的打开文件可以分为许多类别,在不同操作系统下文件的类型也不同,我们以常见的Windows系统为例,他以后缀名区分文件的类型,例如txt为文本文件,mp4为视频文件,exe为可执行文件等众多类别
对于文件的操作也有,打开,关闭,读写,执行等
我们以最简单的txt文本文件为例,对文件的操作就是,打开,读写,关闭
在Python中,我们使用open函数可以打开一个已经存在的文件,或者创建一个新的文件,语法如下
1open(name,mode,encoding)
这里的name是文件名,mode是表示打开文件的方式即访问模式,对应着只读,写入,追加等,encoding表示文件的编码格式
例如
1file = open('text1.txt','...
函数进阶多返回值当在函数中想要返回多个返回值时,写多个return是行不通的,因为在执行第一个return的时候就会退出当前函数
1234def test_return(): return 1,2x,y = test_return()print(f"x={x},y={y}")
按照返回值的顺序,写对应顺序的多个变量接收即可
变量之间用逗号隔开
支持不同类型的数据return
函数的传参位置参数位置参数就是在调用函数时根据定义函数的参数位置来传递参数
1234def func(name,age,gender): print(f"name={name},age={age},gender={gender}") print(func("n",18,"male"))
需要注意的是传递的参数和定义的参数的顺序和个数必须要一致
关键字参数在函数调用时通过“关键字=传入值”的形式进行参数的传递...
推导式推导式其实可以理解为简易版的for循环,是Python独有的特性,它能够非常简洁的按照某种规则以一个序列推导出另一个新的序列,也可以理解为是切片的升级
列表推导式列表的推导式非常简单
1[生成表达式 for 变量 in 序列或迭代对象]
最外层的方括号表明生成结果为列表,在功能上,列表推导式相当于一个循环,只是形式更加简介
例如
1list1 = [x**2 for x in range(4)]
前面我们讲过range的基本使用,这里的代码等效于下面的for循环,这里的**表示乘方
123list1 = []for x in range(4): list1.append(x**2)
数据的过滤在列表推导式中,我们可以使用if语句筛选符合条件的元素,例如我们想提取1到10中平方数为偶数的数
1list2 = [x**2 for x in range(10) if (x**2)%2 == 0]
这里只演示了一层循环,实际上也支持两层循环,但是应用场景较少并且易读性不高,这里不过多介绍了
字典推导式字典和集合不支持切片,推导式则极大的简化了其遍历的过程
字典推导式...